#![feature(trait_alias)]
#![feature(once_cell)]

use im::*;
use std::rc::*;

use ggez::event;
use ggez::graphics;
use ggez::{Context, GameResult};
use glam::*;

mod grid;
mod logic;
mod view;
mod drawing;

use logic::terrian::{BLANK, WALL};

struct MainState {
    dim: Vec2,
    view: view::GameView,
    frame_number: view::FrameNumber,
}

impl MainState {
    fn new(ctx: &mut Context) -> GameResult<MainState> {
        let window_size = {
            let size = graphics::window(ctx).inner_size();
            uvec2(size.width, size.height)
        };

        let floor_size = grid::size(12, 9);

        let terrian_map: grid::Grid<&logic::Terrain> =
            floor_size
                .iter_positions()
                .fold(grid::Grid::empty(floor_size), |g, pos| {
                    let is_edge = pos.x() == 0
                        || pos.x() == floor_size.width - 1
                        || pos.y() == 0
                        || pos.y() == floor_size.height - 1;

                    let is_wall = is_edge || (pos.x() + pos.y()) % 4 == 0;

                    g.set(&pos, if is_wall { &WALL } else { &BLANK })
                });

        let fairy:logic::unit::LittleFairy = logic::unit::LittleFairy::with_hp(100);
        let unit_a: Rc<logic::Unit> = Rc::new(fairy.into());

        let floor = logic::Floor {
            size: floor_size,
            terrian_grid: terrian_map,
            units: vector![unit_a.clone()],
            unit_grid: grid::Grid::empty(floor_size).set(
                &grid::Position::new(floor_size, 1, 1),
                Rc::downgrade(&unit_a),
            ),
        };

        let dim = vec2(window_size.x as f32, window_size.y as f32);

        let view = view::GameView::init(dim, floor);
        let s = MainState {
            dim: vec2(window_size.x as f32, window_size.y as f32),
            view,
            frame_number: 0,
        };
        Ok(s)
    }
}

impl event::EventHandler<ggez::GameError> for MainState {
    fn update(&mut self, _ctx: &mut Context) -> GameResult {
        // let new_view = self.view.clone().next_frame();
        // self.view = new_view;
        self.frame_number += 1;
        self.view = self.view.update(self.frame_number);
        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult {
        graphics::clear(ctx, [0.1, 0.2, 0.3, 1.0].into());
        self.view.draw(&drawing::DrawContext {
            frame_number: self.frame_number,
            dim: self.dim,
            transform: Mat4::IDENTITY,
            ggez_ctx: std::rc::Rc::new(std::cell::RefCell::new(ctx)),
        })?;
        graphics::present(ctx)?;
        Ok(())
    }

    fn key_down_event(
        &mut self,
        _ctx: &mut Context,
        keycode: event::KeyCode,
        _keymods: event::KeyMods,
        _repeat: bool,
    ) {
        let mut inputs: std::collections::HashSet<view::Input> = std::collections::HashSet::new();

        if keycode == event::KeyCode::Up {
            inputs.insert(view::Input::Up);
        }
        if keycode == event::KeyCode::Down {
            inputs.insert(view::Input::Down);
        }
        if keycode == event::KeyCode::Left {
            inputs.insert(view::Input::Left);
        }
        if keycode == event::KeyCode::Right {
            inputs.insert(view::Input::Right);
        }
        self.view = self.view.handle_inputs(self.frame_number, inputs);
    }
}

pub fn main() -> GameResult {
    let cb = ggez::ContextBuilder::new("super_simple", "ggez");
    let (mut ctx, event_loop) = cb.build()?;
    let state = MainState::new(&mut ctx)?;
    event::run(ctx, event_loop, state)
}
